using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using WebHelper.Helpers;

namespace WebHelper.Handlers
{
    [Serializable]
    public class GradientImageHandler : IHttpHandler
    {
        #region Static Implementation

        public static string DefaultPath
        {
            get
            {
                return "GradientImage.axd";
            }
        }
        public static string Width
        {
            get
            {
                return "W";
            }
        }
        public static string Height
        {
            get
            {
                return "H";
            }
        }
        public static string X1
        {
            get
            {
                return "X1";
            }
        }
        public static string X2
        {
            get
            {
                return "X2";
            }
        }
        public static string Y1
        {
            get
            {
                return "Y1";
            }
        }
        public static string Y2
        {
            get
            {
                return "Y2";
            }
        }
        public static string Color1
        {
            get
            {
                return "C1";
            }
        }
        public static string Color2
        {
            get
            {
                return "C2";
            }
        }
        public static string Radius
        {
            get
            {
                return "R";
            }
        }
        public static string BorderColor
        {
            get
            {
                return "BC";
            }
        }
        public static string BorderWidth
        {
            get
            {
                return "BW";
            }
        }
        public static string Alpha
        {
            get
            {
                return "A";
            }
        }


        private static string getHtmlRepresentations(Color color)
        {
            return String.Format("{0:X2}{1:X2}{2:X2}", color.R, color.G, color.B);

        }
        private static Color getColorFromHtml(string s)
        {
            if (Regex.IsMatch(s, "[0-9a-f]{6}", RegexOptions.IgnoreCase))
            {
                s = "#" + s;
            }
            return ColorTranslator.FromHtml(s);
        }
        private static void DrawRoundedRectangle(Graphics gfx, RectangleF bounds, float cornerRadius,float borderWidth , Brush borderColor, Brush FillColor)
        {
            RectangleF fillBounds = RectangleF.Inflate(bounds, -borderWidth, -borderWidth);
            GraphicsPath fillPath = new GraphicsPath();
            fillPath.AddLine(fillBounds.X + cornerRadius, fillBounds.Y, fillBounds.X + fillBounds.Width - (cornerRadius * 2), fillBounds.Y); // Line
            if (cornerRadius > 0)
            {
                fillPath.AddArc(fillBounds.X + fillBounds.Width - cornerRadius, fillBounds.Y, cornerRadius, cornerRadius, 270, 90);
            }
            fillPath.AddLine(fillBounds.X + fillBounds.Width, fillBounds.Y + cornerRadius, fillBounds.X + fillBounds.Width, fillBounds.Height - (cornerRadius * 2)); // Line
            if (cornerRadius > 0)
            {
                fillPath.AddArc(fillBounds.X + fillBounds.Width - cornerRadius, fillBounds.Y + fillBounds.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90);
            }
            fillPath.AddLine(fillBounds.X + fillBounds.Width - (cornerRadius * 2), fillBounds.Y + fillBounds.Height, fillBounds.X + cornerRadius, fillBounds.Y + fillBounds.Height); // Line
            if (cornerRadius > 0)
            {
                fillPath.AddArc(fillBounds.X, fillBounds.Y + fillBounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
            }
            fillPath.AddLine(fillBounds.X, fillBounds.Y + fillBounds.Height - (cornerRadius * 2), fillBounds.X, fillBounds.Y + cornerRadius); // Line
            if (cornerRadius > 0)
            {
                fillPath.AddArc(fillBounds.X, fillBounds.Y, cornerRadius, cornerRadius, 180, 90);
            }
            fillPath.CloseAllFigures();
            gfx.FillPath(FillColor, fillPath);


            //border
            GraphicsPath borderPath = new GraphicsPath();
            borderPath.AddLine(bounds.X + cornerRadius, bounds.Y, bounds.X + bounds.Width - (cornerRadius * 2), bounds.Y); // Line
            if (cornerRadius > 0)
            {
                borderPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y, cornerRadius, cornerRadius, 270, 90);
            }
            borderPath.AddLine(bounds.X + bounds.Width, bounds.Y + cornerRadius, bounds.X + bounds.Width, bounds.Height - (cornerRadius * 2)); // Line
            if (cornerRadius > 0)
            {
                borderPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90);
            }
            borderPath.AddLine(bounds.X + bounds.Width - (cornerRadius * 2), bounds.Y + bounds.Height, bounds.X + cornerRadius, bounds.Y + bounds.Height); // Line
            if (cornerRadius > 0)
            {
                borderPath.AddArc(bounds.X, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
            }
            borderPath.AddLine(bounds.X, bounds.Y + bounds.Height - (cornerRadius * 2), bounds.X, bounds.Y + cornerRadius); // Line
            if (cornerRadius > 0)
            {
                borderPath.AddArc(bounds.X, bounds.Y, cornerRadius, cornerRadius, 180, 90);
            }
            borderPath.AddPath(fillPath, false);
            gfx.FillPath(borderColor, borderPath);
        }
        private static bool IsResourceModified(DateTime currentDate, HttpRequest request)
        {
            bool ResourceModified = true;
            DateTime requestModifydate;
            // Check If-Modified-Since request header, if it exists 
            if (!string.IsNullOrEmpty(request.Headers["If-Modified-Since"]) && DateTime.TryParse(request.Headers["If-Modified-Since"], out requestModifydate))
            {
                ResourceModified = (currentDate.ToString("yyyyMMddHHmmss") != requestModifydate.ToString("yyyyMMddHHmmss"));
            }
            bool ETagChanged = true;
            if (!string.IsNullOrEmpty(request.Headers["If-None-Match"]))
            {
                ETagChanged = request.Headers["If-None-Match"] != "\"GradientHandler" + currentDate.ToString("yyyyMMddHHmmss") + "\"";
            }

            return ResourceModified && ETagChanged;
        }


        public static string Register(bool writeToWebConfig, string filePath)
        {
            string path = GradientImageHandler.DefaultPath;
            if (string.IsNullOrEmpty(filePath))
            {
                filePath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            }
            XDocument document = XDocument.Load(filePath);
            XElement httpHandlers = XmlHelper.CreateIfNotExist(document.Root, "system.web", "httpHandlers");
            var configElement = httpHandlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(GradientImageHandler).FullName));
            if (configElement!=null)
            {
                path = configElement.Attribute("path").Value;
                writeToWebConfig = false;
            }
            XElement handlers = XmlHelper.CreateIfNotExist(document.Root, "system.webServer", "handlers");
            configElement = handlers.Elements("add").FirstOrDefault(e => e.Attribute("type") != null && e.Attribute("type").Value.Contains(typeof(GradientImageHandler).FullName));
            if (configElement!=null)
            {
                path = configElement.Attribute("path").Value;
                writeToWebConfig = false;
            }
            //iis6
            configElement = httpHandlers.Elements("remove").FirstOrDefault(e => e.Attribute("name") != null && e.Attribute("name").Value.Contains(typeof(GradientImageHandler).FullName));
            XElement element = new XElement("add",
                new XAttribute("path", path),
                new XAttribute("type", typeof(GradientImageHandler).FullName + "," + typeof(GradientImageHandler).Assembly.FullName),
                new XAttribute("verb", "*"),
                new XAttribute("validate", "false"));
            httpHandlers.Add(element);

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "register in system.web", typeof(GradientImageHandler).Name + "  ");

            //iis7
            configElement = handlers.Elements("remove").FirstOrDefault(e => e.Attribute("name") != null && e.Attribute("name").Value.Contains(typeof(GradientImageHandler).FullName));
            if(configElement==null)
            {
                if (handlers.Elements("remove").Count() > 0)
                {
                    handlers.Elements("remove").Last().AddAfterSelf(new XElement("remove", new XAttribute("name", typeof(GradientImageHandler).FullName)));
                }
                else
                {
                    handlers.AddFirst(new XElement("remove", new XAttribute("name", typeof(GradientImageHandler).FullName)));
                }
            }
            element = new XElement("add",
                new XAttribute("name", typeof(GradientImageHandler).FullName),
                new XAttribute("path", path),
                new XAttribute("type", typeof(GradientImageHandler).FullName + "," + typeof(GradientImageHandler).Assembly.FullName),
                new XAttribute("verb", "*"),
                new XAttribute("preCondition", "integratedMode"));
            handlers.Add(element);

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "register in system.webServer", typeof(GradientImageHandler).Name + "  ");

            if (writeToWebConfig)
            {
                document.Save(filePath);

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "register and saved at" + filePath, typeof(GradientImageHandler).Name + "  ");

            }
            return element.ToString();
        }
        public static void UnRegister(string filePath)
        {
            if (string.IsNullOrEmpty(filePath))
            {
                filePath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            }
            XDocument document = XDocument.Load(filePath);
            XElement configElement = null;
            XElement httpHandlers = XmlHelper.GetElementsOfPath(document.Root, "system.web", "httpHandlers").FirstOrDefault();
            if (httpHandlers != null)
            {
                configElement = httpHandlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(GradientImageHandler).FullName));
                if (configElement != null)
                {
                    configElement.Remove();
                }
            }
            XElement handlers = XmlHelper.GetElementsOfPath(document.Root, "system.webServer", "handlers").FirstOrDefault();
            if (handlers != null)
            {
                configElement = handlers.Elements("remove").FirstOrDefault(e => e.Attribute("name").Value.Contains(typeof(GradientImageHandler).FullName));
                if (configElement != null)
                {
                    configElement.Remove();
                }
                configElement = handlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(GradientImageHandler).FullName));
                if (configElement != null)
                {
                    configElement.Remove();
                }
            }
            document.Save(filePath);

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "unRegister from web.Congig", typeof(GradientImageHandler).Name + "  ");

        }
        public static string GetUrl(float? width, float? height, float? x1, float? x2, float? y1, float? y2, Color? color1, Color? color2, float? radius, float? borderWidth, Color? borderColor, byte? alpha)
        {
            string path = GradientImageHandler.DefaultPath;
            XDocument document = XDocument.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
            XElement httpHandlers = XmlHelper.CreateIfNotExist(document.Root, "system.web", "httpHandlers");
            var configElement = httpHandlers.Elements("add").FirstOrDefault(e => e.Attribute("type").Value.Contains(typeof(GradientImageHandler).FullName));
            if (configElement != null)
            {
                path = configElement.Attribute("path").Value;
            }
            XElement handlers = XmlHelper.CreateIfNotExist(document.Root, "system.webServer", "handlers");
            configElement = handlers.Elements("add").FirstOrDefault(e => e.Attribute("type") != null && e.Attribute("type").Value.Contains(typeof(GradientImageHandler).FullName));
            if (configElement != null)
            {
                path = configElement.Attribute("path").Value;
            }

            string queryString = "";
            queryString += (width.HasValue && queryString != "" ? "&" : "") + (width.HasValue ? GradientImageHandler.Width + "=" + width.Value : "");
            queryString += (height.HasValue && queryString != "" ? "&" : "") + (height.HasValue ? GradientImageHandler.Height + "=" + height.Value : "");
            queryString += (x1.HasValue && queryString != "" ? "&" : "") + (x1.HasValue ? GradientImageHandler.X1 + "=" + x1.Value : "");
            queryString += (x2.HasValue && queryString != "" ? "&" : "") + (x2.HasValue ? GradientImageHandler.X2 + "=" + x2.Value : "");
            queryString += (y1.HasValue && queryString != "" ? "&" : "") + (y1.HasValue ? GradientImageHandler.Y1 + "=" + y1.Value : "");
            queryString += (y2.HasValue && queryString != "" ? "&" : "") + (y2.HasValue ? GradientImageHandler.Y2 + "=" + y2.Value : "");
            queryString += (color1.HasValue && !color1.Value.IsEmpty && queryString != "" ? "&" : "") + (color1.HasValue && !color1.Value.IsEmpty ? GradientImageHandler.Color1 + "=" + getHtmlRepresentations(color1.Value) : "");
            queryString += (color2.HasValue && !color2.Value.IsEmpty && queryString != "" ? "&" : "") + (color2.HasValue && !color2.Value.IsEmpty ? GradientImageHandler.Color2 + "=" + getHtmlRepresentations(color2.Value) : "");
            queryString += (radius.HasValue && queryString != "" ? "&" : "") + (radius.HasValue ? GradientImageHandler.Radius + "=" + radius.Value : "");
            queryString += (borderWidth.HasValue && queryString != "" ? "&" : "") + (borderWidth.HasValue ? GradientImageHandler.BorderWidth + "=" + borderWidth.Value : "");
            queryString += (borderColor.HasValue && !borderColor.Value.IsEmpty && queryString != "" ? "&" : "") + (borderColor.HasValue && !borderColor.Value.IsEmpty ? GradientImageHandler.BorderColor + "=" + getHtmlRepresentations(borderColor.Value) : "");
            queryString += (alpha.HasValue && queryString != "" ? "&" : "") + (alpha.HasValue ? GradientImageHandler.Alpha + "=" + alpha.Value : "");
            
            return path + (queryString != "" ? "?" : "") + queryString;
        }
        public static Bitmap GetBitmap(float? width, float? height, float? x1, float? x2, float? y1, float? y2, Color? color1, Color? color2, float? radius, float? borderWidth, Color? borderColor, byte? alpha)
        {
            //default values
            float widthValue = width.HasValue ? width.Value : 1;
            float heightValue = height.HasValue ? height.Value : 1;
            float x1Value = x1.HasValue ? x1.Value : 0;
            float x2Value = x2.HasValue ? x2.Value : widthValue;
            float y1Value = y1.HasValue ? y1.Value : 0;
            float y2Value = y2.HasValue ? y2.Value : heightValue;
            byte alphaValue = alpha.HasValue ? Convert.ToByte(alpha) : (byte)255;
            Color color1Value = color1.HasValue ? Color.FromArgb(alphaValue, color1.Value) : Color.Empty;
            Color color2Value = color2.HasValue ? Color.FromArgb(alphaValue, color2.Value) : Color.Empty;
            float radiusValue = radius.HasValue ? radius.Value : 0;
            Color borderColorValue = borderColor.HasValue ? Color.FromArgb(alphaValue, borderColor.Value) : Color.Empty;
            float borderWidthValue = borderWidth.HasValue ? borderWidth.Value : 0;

            Bitmap bitmap = new Bitmap((int)widthValue, (int)heightValue);

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "initialize bitmap with width & height", typeof(GradientImageHandler).Name + "  ");

            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.CompositingQuality = CompositingQuality.HighQuality;

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "initialize graphic from bitmap", typeof(GradientImageHandler).Name + "  ");

                using (Brush fillBrush = new LinearGradientBrush(new PointF(x1Value, y1Value), new PointF(x2Value, y2Value), color1Value, color2Value))
                {
                    using (Brush borderBrush = new SolidBrush(borderColorValue))
                    {
                        GradientImageHandler.DrawRoundedRectangle(graphics, new RectangleF(0, 0, bitmap.Width, bitmap.Height), radiusValue, borderWidthValue, borderBrush, fillBrush);
                    }
                }

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "drow on the graphic", typeof(GradientImageHandler).Name + "  ");
            }
            return bitmap;
        }
        #endregion


        public bool IsReusable
        {
            get { return true; }
        }
        public void ProcessRequest(HttpContext context)
        {
            //cashing
            DateTime lastModifieddate = new DateTime(2009, 9, 2);
            if (GradientImageHandler.IsResourceModified(lastModifieddate, context.Request))
            {
                string widthParam = context.Request.QueryString[GradientImageHandler.Width];
                string heightParam = context.Request.QueryString[GradientImageHandler.Height];
                string x1Param = context.Request.QueryString[GradientImageHandler.X1];
                string x2Param = context.Request.QueryString[GradientImageHandler.X2];
                string y1Param = context.Request.QueryString[GradientImageHandler.Y1];
                string y2Param = context.Request.QueryString[GradientImageHandler.Y2];
                string alphaParam = context.Request.QueryString[GradientImageHandler.Alpha];
                string color1Param = context.Request.QueryString[GradientImageHandler.Color1];
                string color2Param = context.Request.QueryString[GradientImageHandler.Color2];
                string radiusParam = context.Request.QueryString[GradientImageHandler.Radius];
                string borderColorParam = context.Request.QueryString[GradientImageHandler.BorderColor];
                string borderwidthParam = context.Request.QueryString[GradientImageHandler.BorderWidth];


                // parse query string parameters
                float? widthValue = (string.IsNullOrEmpty(widthParam) ? null : (float?)Convert.ToSingle(widthParam));
                float? heightValue = (string.IsNullOrEmpty(heightParam) ? null : (float?)Convert.ToSingle(heightParam));
                float? x1Value = (string.IsNullOrEmpty(x1Param) ? null : (float?)Convert.ToSingle(x1Param));
                float? x2Value = (string.IsNullOrEmpty(x2Param) ? null : (float?)Convert.ToSingle(x2Param));
                float? y1Value = (string.IsNullOrEmpty(y1Param) ? null : (float?)Convert.ToSingle(y1Param));
                float? y2Value = (string.IsNullOrEmpty(y2Param) ? null : (float?)Convert.ToSingle(y2Param));
                byte? alphaValue = (string.IsNullOrEmpty(alphaParam) ? null : (byte?)Convert.ToByte(alphaParam));
                Color? color1Value = (string.IsNullOrEmpty(color1Param) ? null : (Color?)getColorFromHtml(color1Param));
                Color? color2Value = (string.IsNullOrEmpty(color2Param) ? null : (Color?)getColorFromHtml(color2Param));
                float? radiusValue = (string.IsNullOrEmpty(radiusParam) ? null : (float?)Convert.ToSingle(radiusParam));
                Color? borderColorValue = (string.IsNullOrEmpty(borderColorParam) ? null : (Color?)getColorFromHtml(borderColorParam));
                float? borderWidthValue = (string.IsNullOrEmpty(borderwidthParam) ? null : (float?)Convert.ToSingle(borderwidthParam));

                using (Bitmap bitmap = GradientImageHandler.GetBitmap(widthValue, heightValue, x1Value, x2Value, y1Value, y2Value, color1Value, color2Value, radiusValue, borderWidthValue, borderColorValue, alphaValue))
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        // memory stream required because of bug in GDI+ when saving PNG directly to Response.OutputStream
                        bitmap.Save(memoryStream, ImageFormat.Png);
                        context.Response.ContentType = "image/png";
                        memoryStream.WriteTo(context.Response.OutputStream);
                    }
                }

                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "build bitmap", typeof(GradientImageHandler).Name + "  ");

            }
            else
            {
                context.Response.StatusCode = 304;
                context.Response.StatusDescription = "Not Modified";
                // Explicitly set the Content-Length header so the client doesn't wait for
                //  content but keeps the connection open for other requests 
                context.Response.AddHeader("Content-Length", "0");


                //trace
                Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "no build for bitmap,send not modified to browser", typeof(GradientImageHandler).Name + "  ");

            }

            //cashing
            context.Response.Cache.SetCacheability(HttpCacheability.Public);
            context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
            context.Response.Cache.SetMaxAge(new TimeSpan(0, 0, 0));
            try
            {
                context.Response.Cache.SetLastModified(lastModifieddate);
            }
            catch
            {
            }
            context.Response.Cache.SetETag("\"GradientHandler" + lastModifieddate.ToString("yyyyMMddHHmmss") + "\"");
            context.Response.Cache.VaryByParams["*"] = true;
            context.Response.Cache.VaryByHeaders["If-Modified-Since"] = true;
            context.Response.Cache.VaryByHeaders["If-None-Match"] = true;
            context.Response.End();

            //trace
            Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "write response cash headers", typeof(GradientImageHandler).Name + "  ");

        }

    }
}
